package br.senai.sc.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.OrderBy;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;

/**
 * @author Fernando Marquardt &lt;fernando.marquardt@sc.senai.br&gt;
 * @since 31/01/PRECISION12
 * @version $Id: PontoColeta.java 30 PRECISION12-03-08 PRECISION:16:18Z
 *          fernando.marquardt $
 */
@Entity
@Table(name = "ponto_coleta")
@NamedQueries({
		@NamedQuery(name = PontoColeta.PONTOS_COLETA, query = "SELECT pc FROM PontoColeta pc ORDER BY pc.nome"),
		@NamedQuery(name = PontoColeta.ULTIMAS_AMOSTRAS, query = "SELECT a FROM Amostra a WHERE a.pontoColeta.idPontoColeta = :idPontoColeta ORDER BY a.data DESC"),
		@NamedQuery(name = PontoColeta.MAIOR_E_MENOR_LATITUDE_E_LONGITUDE, query = "SELECT MAX(pc.latitude), MIN(pc.latitude), MAX(pc.longitude), MIN(pc.longitude) FROM PontoColeta pc "
				+ "WHERE pc.latitude <> 0 AND pc.longitude <> 0"),
		@NamedQuery(name = PontoColeta.PONTOS_COLETA_BY_CIDADE, query = "SELECT pc FROM PontoColeta pc WHERE pc.cidade = :cidade") })
public class PontoColeta implements Serializable {

	private static final long serialVersionUID = 1L;

	public static final String PONTOS_COLETA = "PONTOS_COLETA";
	public static final String ULTIMAS_AMOSTRAS = "ULTIMA_AMOSTRA";

	/**
	 * Busca a coordenada de maior latitude, menor latitude, maior longitude e
	 * menor longitude.
	 */
	public static final String MAIOR_E_MENOR_LATITUDE_E_LONGITUDE = "MAIOR_E_MENOR_LATITUDE_E_LONGITUDE";
	public static final String PONTOS_COLETA_BY_CIDADE = "PONTOS_COLETA_BY_CIDADE";

	/**
	 * Precisão utilizadas para os parâmetros.
	 */
	private static final int PRECISION = 20;

	/**
	 * Escala utilizada para os parametros.
	 */
	private static final int SCALE = 14;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id_ponto_coleta")
	private Integer idPontoColeta;

	@NotNull
	@ManyToOne
	@JoinColumn(name = "id_cidade")
	private Cidade cidade;

	@NotNull
	@ManyToOne
	@JoinColumn(name = "id_corpo_hidrico")
	private CorpoHidrico corpoHidrico;

	@NotNull
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal altitude;

	@NotNull
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal latitude;

	@NotNull
	@Column(precision = PRECISION, scale = SCALE)
	private BigDecimal longitude;

	@NotBlank
	@Length(max = 150)
	private String nome;

	@Length(max = 120)
	@Column(name = "ponto_referencia")
	private String pontoReferencia;

	@Length(max = 150)
	private String descricao;

	@OneToMany(mappedBy = "pontoColeta")
	@OrderBy(clause = "data")
	private List<Amostra> amostras;

	// TODO Solução temporária, deve ser encontrado um jeito melhor.
	@Transient
	private Amostra ultimaAmostra;

	@ManyToMany
	@JoinTable(name = "ponto_coleta_foto", joinColumns = { @JoinColumn(name = "id_ponto_coleta") }, inverseJoinColumns = { @JoinColumn(name = "id_foto") })
	@Cascade(value = CascadeType.ALL)
	private List<Foto> fotos = new ArrayList<Foto>();

	public PontoColeta() {
	}

	public Integer getIdPontoColeta() {
		return idPontoColeta;
	}

	public void setIdPontoColeta(Integer idPontoColeta) {
		this.idPontoColeta = idPontoColeta;
	}

	public Cidade getCidade() {
		return cidade;
	}

	public void setCidade(Cidade cidade) {
		this.cidade = cidade;
	}

	public CorpoHidrico getCorpoHidrico() {
		return corpoHidrico;
	}

	public void setCorpoHidrico(CorpoHidrico corpoHidrico) {
		this.corpoHidrico = corpoHidrico;
	}

	public BigDecimal getAltitude() {
		return altitude;
	}

	public void setAltitude(BigDecimal altitude) {
		this.altitude = altitude;
	}

	public BigDecimal getLatitude() {
		return latitude;
	}

	public void setLatitude(BigDecimal latitude) {
		this.latitude = latitude;
	}

	public BigDecimal getLongitude() {
		return longitude;
	}

	public void setLongitude(BigDecimal longitude) {
		this.longitude = longitude;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getPontoReferencia() {
		return pontoReferencia;
	}

	public void setPontoReferencia(String pontoReferencia) {
		this.pontoReferencia = pontoReferencia;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public Amostra getUltimaAmostra() {
		return ultimaAmostra;
	}

	public void setUltimaAmostra(Amostra ultimaAmostra) {
		this.ultimaAmostra = ultimaAmostra;
	}

	public List<Amostra> getAmostras() {
		return amostras;
	}

	public void setAmostras(List<Amostra> amostras) {
		this.amostras = amostras;
	}

	public List<Foto> getFotos() {
		return fotos;
	}

	public void setFotos(List<Foto> fotos) {
		this.fotos = fotos;
	}

	@Override
	public String toString() {
		return "PontoColeta [idPontoColeta=" + idPontoColeta + ", altitude="
				+ altitude + ", latitude=" + latitude + ", longitude="
				+ longitude + ", nome=" + nome + ", descricao=" + descricao
				+ "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = (prime * result) + ((cidade == null) ? 0 : cidade.hashCode());
		result = (prime * result)
				+ ((corpoHidrico == null) ? 0 : corpoHidrico.hashCode());
		result = (prime * result) + ((nome == null) ? 0 : nome.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		PontoColeta other = (PontoColeta) obj;
		if (cidade == null) {
			if (other.cidade != null) {
				return false;
			}
		} else if (!cidade.equals(other.cidade)) {
			return false;
		}
		if (corpoHidrico == null) {
			if (other.corpoHidrico != null) {
				return false;
			}
		} else if (!corpoHidrico.equals(other.corpoHidrico)) {
			return false;
		}
		if (nome == null) {
			if (other.nome != null) {
				return false;
			}
		} else if (!nome.equals(other.nome)) {
			return false;
		}
		return true;
	}

}